module user_ip (
  output tri0        spi_clk,
  output tri0        spi_csn,
  output tri0        spi_led,
  input              spi_miso,
  output tri0        spi_mosi,
  inout       [15:0] z80_addr,
  output tri0        z80_busreq_n,
  inout       [7:0]  z80_data,
  inout              z80_iorq_n,
  inout              z80_m1,
  inout              z80_mreq_n,
  inout              z80_rd_n,
  output tri0        z80_reset_n,
  output tri0        z80_wait_n,
  inout              z80_wr_n,
  output tri0        iocvt_ch4_0_in,
  input              sys_clock,
  input              bus_clock,
  input              resetn,
  input              stop,
  input       [1:0]  mem_ahb_htrans,
  input              mem_ahb_hready,
  input              mem_ahb_hwrite,
  input       [31:0] mem_ahb_haddr,
  input       [2:0]  mem_ahb_hsize,
  input       [2:0]  mem_ahb_hburst,
  input       [31:0] mem_ahb_hwdata,
  output tri1        mem_ahb_hreadyout,
  output tri0        mem_ahb_hresp,
  output tri0 [31:0] mem_ahb_hrdata,
  output tri0        slave_ahb_hsel,
  output tri1        slave_ahb_hready,
  input              slave_ahb_hreadyout,
  output tri0 [1:0]  slave_ahb_htrans,
  output tri0 [2:0]  slave_ahb_hsize,
  output tri0 [2:0]  slave_ahb_hburst,
  output tri0        slave_ahb_hwrite,
  output tri0 [31:0] slave_ahb_haddr,
  output tri0 [31:0] slave_ahb_hwdata,
  input              slave_ahb_hresp,
  input       [31:0] slave_ahb_hrdata,
  output tri0 [3:0]  ext_dma_DMACBREQ,
  output tri0 [3:0]  ext_dma_DMACLBREQ,
  output tri0 [3:0]  ext_dma_DMACSREQ,
  output tri0 [3:0]  ext_dma_DMACLSREQ,
  input       [3:0]  ext_dma_DMACCLR,
  input       [3:0]  ext_dma_DMACTC,
  output tri0 [3:0]  local_int
);

//----------------------------------------------------------------------------
// APB Interface Signals
//----------------------------------------------------------------------------
wire                 apb_psel;         // APB peripheral select signal
wire                 apb_penable;      // APB enable signal (ACCESS phase)
wire                 apb_pwrite;       // APB write enable signal
wire [31:0]          apb_paddr;        // APB address bus
wire [31:0]          apb_pwdata;       // APB write data bus
wire [3:0]           apb_pstrb;        // APB byte strobe signals
wire [2:0]           apb_pprot;        // APB protection control
wire                 apb_pready;       // Slave ready response
wire                 apb_pslverr;      // Slave error response
wire [31:0]          apb_prdata;       // APB read data bus

//----------------------------------------------------------------------------
// AHB-to-APB Bridge
//----------------------------------------------------------------------------
ahb2apb u_ahb2apb (
  .reset        (!resetn                     ),
  .ahb_clock    (sys_clock                   ),
  .ahb_hmastlock(1'b0                        ),
  .ahb_htrans   (mem_ahb_htrans              ),
  .ahb_hsel     (1'b1                        ),
  .ahb_hready   (mem_ahb_hready              ),
  .ahb_hwrite   (mem_ahb_hwrite              ),
  .ahb_haddr    (mem_ahb_haddr               ),
  .ahb_hsize    (mem_ahb_hsize               ),
  .ahb_hburst   (mem_ahb_hburst              ),
  .ahb_hprot    (4'b0011                     ),
  .ahb_hwdata   (mem_ahb_hwdata              ),
  .ahb_hrdata   (mem_ahb_hrdata              ),
  .ahb_hreadyout(mem_ahb_hreadyout           ),
  .ahb_hresp    (mem_ahb_hresp               ),
  .apb_clock    (bus_clock                   ),
  .apb_psel     (apb_psel                    ),
  .apb_penable  (apb_penable                 ),
  .apb_pwrite   (apb_pwrite                  ),
  .apb_paddr    (apb_paddr                   ),
  .apb_pwdata   (apb_pwdata                  ),
  .apb_pstrb    (apb_pstrb                   ),
  .apb_pprot    (apb_pprot                   ),
  .apb_pready   (apb_pready                  ),
  .apb_pslverr  (apb_pslverr                 ),
  .apb_prdata   (apb_prdata                  )
);

//----------------------------------------------------------------------------
// Z80 /RESET Control Register
//----------------------------------------------------------------------------
wire [31:0] reg_reset;
wire pready_reg_reset;

z80_reg #(.ADDR(24'h000004)) u_z80_reg_reset (
  .clk             (bus_clock               ),
  .rst_n           (resetn                  ),
  .apb_psel_in     (apb_psel                ),
  .apb_penable_in  (apb_penable             ),
  .apb_pwrite_in   (apb_pwrite              ), 
  .apb_paddr_in    (apb_paddr               ),
  .apb_pwdata_in   (apb_pwdata              ),
  .data_out        (reg_reset               ),
  .apb_pready_out  (pready_reg_reset        )
);

//----------------------------------------------------------------------------
// Z80 /BUSREQ Control Register
//----------------------------------------------------------------------------
wire [31:0] reg_busreq;
wire pready_reg_busreq;
z80_reg #(.ADDR(24'h00000C)) u_z80_reg_busreq (
  .clk             (bus_clock               ),
  .rst_n           (resetn                  ),
  .apb_psel_in     (apb_psel                ),
  .apb_penable_in  (apb_penable             ),
  .apb_pwrite_in   (apb_pwrite              ),
  .apb_paddr_in    (apb_paddr               ),
  .apb_pwdata_in   (apb_pwdata              ),
  .data_out        (reg_busreq              ),
  .apb_pready_out  (pready_reg_busreq       )
);

//----------------------------------------------------------------------------
// MCU to Z80 Data Register
//----------------------------------------------------------------------------
wire pready_reg_data;
wire [31:0] reg_data;
z80_reg #(.ADDR(24'h000000)) u_z80_reg_data (
  .clk               (bus_clock             ),
  .rst_n             (resetn                ),
  .apb_psel_in       (apb_psel              ),
  .apb_penable_in    (apb_penable           ),
  .apb_pwrite_in     (apb_pwrite            ),
  .apb_paddr_in      (apb_paddr             ),
  .apb_pwdata_in     (apb_pwdata            ),
  .data_out          (reg_data              ),
  .apb_pready_out    (pready_reg_data       )
);

//----------------------------------------------------------------------------
// SPI0 Clock Divider Register
//----------------------------------------------------------------------------
wire pready_reg_spi0_clk_div;
wire [31:0] reg_spi0_clk_div;
z80_reg #(.ADDR(24'h000018)) u_z80_reg_spi0_clk_div (
  .clk               (bus_clock              ),
  .rst_n             (resetn                 ),
  .apb_psel_in       (apb_psel               ),
  .apb_penable_in    (apb_penable            ),
  .apb_pwrite_in     (apb_pwrite             ),
  .apb_paddr_in      (apb_paddr              ),
  .apb_pwdata_in     (apb_pwdata             ),
  .data_out          (reg_spi0_clk_div       ),
  .apb_pready_out    (pready_reg_spi0_clk_div)
);

//----------------------------------------------------------------------------
// Z80 /WAIT Control
//----------------------------------------------------------------------------
wire pready_reg_wait;
wire        reg_wait;
z80_wait_ctrl #(.ADDR(24'h000008)) u_z80_wait_ctrl (
  .clk               (bus_clock             ),
  .rst_n             (resetn                ),
  .apb_psel_in       (apb_psel              ),
  .apb_penable_in    (apb_penable           ),
  .apb_pwrite_in     (apb_pwrite            ),
  .apb_paddr_in      (apb_paddr             ),
  .apb_pwdata_in     (apb_pwdata            ),
  .z80_iorq_n_in     (z80_iorq_n            ),
  .z80_m1_in         (z80_m1                ),
  .wait_out          (reg_wait              ),
  .apb_pready_out    (pready_reg_wait       )
);

//----------------------------------------------------------------------------
// Z80 SRAM Control Register
//----------------------------------------------------------------------------
wire pready_sram_ctrl;
wire [31:0] sram_ad_out;
wire        sram_cs_out;
wire        sram_we_out;
wire        sram_oe_out;

z80_sram_ctrl #(.ADDR(24'h000010)) u_z80_sram_ctrl (
  .clk             (bus_clock               ),
  .rst_n           (resetn                  ),
  .apb_psel_in     (apb_psel                ),
  .apb_penable_in  (apb_penable             ),
  .apb_pwrite_in   (apb_pwrite              ),
  .apb_paddr_in    (apb_paddr               ),
  .apb_pwdata_in   (apb_pwdata              ),
  .apb_pready_out  (pready_sram_ctrl        ),
  .sram_ad_out     (sram_ad_out             ),
  .sram_cs_out     (sram_cs_out             ),
  .sram_we_out     (sram_we_out             ),
  .sram_oe_out     (sram_oe_out             )
);


//----------------------------------------------------------------------------
// Z80 Bus Decode
//----------------------------------------------------------------------------
wire [7:0] z80_dec_addr;
wire [7:0] z80_dec_data;
wire       z80_dec_rd;
wire       z80_dec_wr;
wire       mcu_irq_pulse;

z80_bus_decode u_z80_bus_decode(
  .clk               (bus_clock             ),
  .rst_n             (resetn                ),
  .z80_addr_in       (z80_addr              ),
  .z80_data_in       (z80_data              ),
  .z80_iorq_n_in     (z80_iorq_n            ),
  .z80_m1_in         (z80_m1                ),
  .z80_mreq_n_in     (z80_mreq_n            ),
  .z80_rd_n_in       (z80_rd_n              ),
  .z80_wr_n_in       (z80_wr_n              ),
  .z80_dec_addr_out  (z80_dec_addr          ),
  .z80_dec_data_out  (z80_dec_data          ),
  .z80_dec_rd_out    (z80_dec_rd            ),
  .z80_dec_wr_out    (z80_dec_wr            ),
  .mcu_irq_pulse_out (mcu_irq_pulse         )
);

// MCU interrupt line, triggered on falling edge
assign iocvt_ch4_0_in = mcu_irq_pulse;

//----------------------------------------------------------------------------
// SPI0 (Micro SD) Control
//----------------------------------------------------------------------------
wire pready_sd;
wire [31:0] sd_rx_data;
z80_sd u_z80_sd (
  .clk             (bus_clock               ),
  .rst_n           (resetn                  ),
  .apb_psel_in     (apb_psel                ),
  .apb_penable_in  (apb_penable             ),
  .apb_pwrite_in   (apb_pwrite              ),
  .apb_paddr_in    (apb_paddr               ),
  .apb_pwdata_in   (apb_pwdata              ),
  .apb_prdata_out  (sd_rx_data              ),
  .apb_pready_out  (pready_sd               ),
  .spi_clk_div     (reg_spi0_clk_div        ),
  .spi_miso_in     (spi_miso                ),
  .spi_mosi_out    (spi_mosi                ),
  .spi_clk_out     (spi_clk                 ),
  .spi_csn_out     (spi_csn                 ),
  .spi_led_out     (spi_led                 )
);

//----------------------------------------------------------------------------
// APB R/W Routing and Wait Logic
//----------------------------------------------------------------------------
wire [31:0] mcu_data;
assign mcu_data   = {z80_dec_wr, 15'b0, z80_dec_addr[7:0], z80_dec_data[7:0]};

assign apb_prdata = (apb_psel && apb_paddr[23:0] == 24'h000000) ? mcu_data          : 
                    (apb_psel && apb_paddr[23:0] == 24'h000014) ? sd_rx_data        : 32'b0;

// APB ready routing (only when selected)
assign apb_pready = (apb_psel && apb_paddr[23:0] == 24'h000000) ? pready_reg_data         :
                    (apb_psel && apb_paddr[23:0] == 24'h000004) ? pready_reg_reset        :
                    (apb_psel && apb_paddr[23:0] == 24'h000008) ? pready_reg_wait         :
                    (apb_psel && apb_paddr[23:0] == 24'h00000C) ? pready_reg_busreq       :
                    (apb_psel && apb_paddr[23:0] == 24'h000010) ? pready_sram_ctrl        :
                    (apb_psel && apb_paddr[23:0] == 24'h000014) ? pready_sd               :
                    (apb_psel && apb_paddr[23:0] == 24'h000018) ? pready_reg_spi0_clk_div : 1'b1;

assign apb_pslverr = 1'b0;

// Z80 Bus Drive
assign z80_addr     = reg_busreq[0] ? sram_ad_out[31:16]: 16'bz;
assign z80_data     = reg_busreq[0] ? sram_ad_out[7:0] : (z80_dec_rd ? reg_data[7:0] : 8'bz);
                      
assign z80_m1       = reg_busreq[0] ? 1'b1 : 1'bz;
assign z80_iorq_n   = reg_busreq[0] ? 1'b1 : 1'bz;
assign z80_mreq_n   = reg_busreq[0] ? ~sram_cs_out : 1'bz;
assign z80_wr_n     = reg_busreq[0] ? ~sram_we_out : 1'bz;
assign z80_rd_n     = reg_busreq[0] ? ~sram_oe_out : 1'bz;
assign z80_busreq_n = ~reg_busreq[0];
assign z80_reset_n  = ~reg_reset[0];
assign z80_wait_n   = ~reg_wait;

endmodule
